﻿using System;
using System.IO;
using System.Security.AccessControl;
using System.Security.Principal;

namespace SuppKids.Core.Security.File
{
    public class AccessibleFileStorage : FileStorage
	{
		public override void WriteAllBytes(string path, byte[] bytes)
		{
			this.EnsureDirectoryExistence(path, true);

			bool fileCreated = false;
			var file = new FileInfo(path);
			if (!file.Exists)
			{
				fileCreated = true;
			}

			base.WriteAllBytes(path, bytes);

			if (fileCreated)
			{
				file.Refresh();
				if (file.Exists)
				{
					this.AllowFullControlToEveryone(file);
				}
			}
		}

		public override void WriteAllText(string path, string contents)
		{
			this.EnsureDirectoryExistence(path, true);

			bool fileCreated = false;
			var file = new FileInfo(path);
			if (!file.Exists)
			{
				fileCreated = true;
			}

			base.WriteAllText(path, contents);

			if (fileCreated)
			{
				file.Refresh();
				if (file.Exists)
				{
					this.AllowFullControlToEveryone(file);
				}
			}
		}

		public virtual bool CreateDirectoryAndCheckAccessRights(string directoryPath)
		{
			return this.CreateDirectoryAndCheckAccessRights(directoryPath, false);
		}

		public virtual bool CreateDirectoryForFileAndCheckDirectoryAccessRights(string filePath)
		{
			return this.CreateDirectoryAndCheckAccessRights(filePath, true);
		}

		private bool CreateDirectoryAndCheckAccessRights(string path, bool isFile)
		{
			bool hasAccessRights;

			try
			{
				this.EnsureDirectoryExistence(path, isFile);

				string directoryName = isFile ? new FileInfo(path).DirectoryName : path;
				string permissionsFile = Path.Combine(directoryName, "permissions.test");
				System.IO.File.WriteAllText(permissionsFile, "test");
				System.IO.File.ReadAllText(permissionsFile);
				System.IO.File.Delete(permissionsFile);
				hasAccessRights = true;
			}
			catch (Exception)
			{
				hasAccessRights = false;
			}

			return hasAccessRights;
		}

		protected virtual void EnsureDirectoryExistence(string path, bool isFile)
		{
			DirectoryInfo directory;
			if (isFile)
			{
				var fileInfo = new FileInfo(path);
				directory = new DirectoryInfo(fileInfo.DirectoryName);
			}
			else
			{
				directory = new DirectoryInfo(path);
			}

			if (!directory.Exists)
			{
				directory.Create();
				this.AllowFullControlToEveryone(directory);
			}
		}

		private void AllowFullControlToEveryone(DirectoryInfo directory)
		{
			DirectorySecurity directorySecurity = directory.GetAccessControl();
			var everyone = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
			var rule = new FileSystemAccessRule(
				everyone,
				FileSystemRights.FullControl,
				InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit,
				PropagationFlags.None,
				AccessControlType.Allow);
			directorySecurity.AddAccessRule(rule);
			directory.SetAccessControl(directorySecurity);
		}

		private void AllowFullControlToEveryone(FileInfo file)
		{
			FileSecurity fileSecurity = file.GetAccessControl();
			var everyone = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
			var rule = new FileSystemAccessRule(
				everyone, FileSystemRights.FullControl, AccessControlType.Allow);
			fileSecurity.AddAccessRule(rule);
			file.SetAccessControl(fileSecurity);
		}
	}
}
